package com.xiaoyi.sm;

/**
 * @author xiaoyi
 * @version 1.0.0
 * @date 2023/07/31 09:51:00
 */
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.Security;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;

public class SM4 {
    private static final String name="SM4";                               //算法名字
    private static final String transformation="SM4/CBC/PKCS5Padding";    //加密模式以及短快填充方式
    private static final String Default_iv="!@#$%^&*)(1a2b3c";            //加密使用的初始向量
    static {
        // 防止内存中出现多次BouncyCastleProvider的实例
        if (null == Security.getProvider(BouncyCastleProvider.PROVIDER_NAME)) {
            Security.addProvider(new BouncyCastleProvider());
        }
    }
    /**
     * 加载指定文件，对其进行加密，并将加密结果写入指定输出文件中
     * @param inputFile 要加密的输入文件路径
     * @param outputFile 加密后的输出文件路径
     * @param key 加密所需的密钥
     * @throws Exception 如果文件读取、加密或写入时出现错误，则抛出异常
     */
    private static void encodeFile(String inputFile, String outputFile, String key) throws Exception {
        // 读取输入文件中的所有字节
        byte [] inputBytes = Files.readAllBytes(Paths.get(inputFile));
        // 对输入字节数组进行加密
        byte [] encodeByte = encode(inputBytes, key.getBytes(StandardCharsets.UTF_8));
        // 将加密后的字节数组写入指定输出文件中
        Files.write(Paths.get(outputFile),encodeByte);
        System.out.println("File encoded successfully.");
    }


    public static String encrypt(String value, String password) throws Exception {
        byte [] inputvalue = value.getBytes(Charset.defaultCharset());
        byte [] encodeByte = encode(inputvalue, password.getBytes(StandardCharsets.UTF_8));
        return Hex.toHexString(encodeByte);
    }


    /**
     * 使用指定的加密算法和密钥对给定的字节数组进行加密
     * @param inputByte 要加密的字节数组
     * @param key 加密所需的密钥
     * @return 加密后的字节数组
     * @throws Exception 如果加密时发生错误，则抛出异常
     */
    public static byte [] encode(byte [] inputByte, byte [] key) throws Exception {
        // 获取加密实例
        Cipher c = Cipher.getInstance(transformation);
        // 根据密钥的字节数组创建 SecretKeySpec
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, name);
        // 创建 IvParameterSpec 对象，使用默认向量和字符集
        IvParameterSpec ivParameterSpec = new IvParameterSpec(Default_iv.getBytes(StandardCharsets.UTF_8));
        // 初始化加密实例
        c.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
        // 返回加密后的字节数组
        return c.doFinal(inputByte);
    }


    public static void decodeFile(String inputFilePath, String outputFilePath, String key) throws Exception {
        byte[] inputBytes = Files.readAllBytes(Paths.get(inputFilePath));
        byte[] decodeBytes = decode(inputBytes, key.getBytes(StandardCharsets.UTF_8));
        Files.write(Paths.get(outputFilePath), decodeBytes);
        System.out.println("File decode successfully.");
    }
    public static String decrypt(String value, String password) throws Exception {
        byte [] inputvalue = value.getBytes(Charset.defaultCharset());
        byte [] encodeByte = decode(inputvalue, password.getBytes(StandardCharsets.UTF_8));
        return Hex.toHexString(encodeByte);
    }

    private static byte[] decode(byte[] inputBytes, byte[] key) throws Exception {
        Cipher cipher = Cipher.getInstance(transformation);
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, name);
        IvParameterSpec ivParameterSpec = new IvParameterSpec(Default_iv.getBytes(StandardCharsets.UTF_8));
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
        return cipher.doFinal(inputBytes);
    }
    /**
     * 生成密钥
     * <p>建议使用org.bouncycastle.util.encoders.Hex将二进制转成HEX字符串</p>
     *
     * @return 密钥16位
     * @throws Exception 生成密钥异常
     */
    public static byte[] generateKey() throws Exception {
        KeyGenerator kg = KeyGenerator.getInstance(name, BouncyCastleProvider.PROVIDER_NAME);
        kg.init(128, new SecureRandom());
        return kg.generateKey().getEncoded();
    }
    public static void main(String[] args) throws Exception {

       String value = "这是中文和english结合的数据";
       String password = "1234567812345678";//Hex.toHexString(generateKey());
       System.out.println("密文:"+password);
       String encrypt = encrypt(value, password);
       System.out.println("加密后的数据:"+encrypt);
       String decrypt = decrypt(encrypt, password);
        System.out.println("解密后的数据:"+decrypt);

    }
}
